//
//  ui.h
//  trail-map-editor
//
//  Created by Giorgio Tino on 2/28/13.
//
//

#ifndef __trail_map_editor__ui__
#define __trail_map_editor__ui__

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string>
#include <map>

#ifdef FREEGLUT_IS_PRESENT
#  include <GL/freeglut.h>
#else
#  ifdef __APPLE__
#    include <GLUT/glut.h>
#  else
#    include <GL/glut.h>
#  endif
#endif

#import <OpenGL/glext.h>

#include <plib/ul.h>
#include <plib/pu.h>
#include <plib/puAux.h>
#include <plib/ssg.h>
#include <plib/pw.h>
#include <plib/js.h>

#include "tinyxml2/tinyxml2.h"

// -------------------------------------------------------------------------------------------------
//  ui - Static Methods
// -------------------------------------------------------------------------------------------------
#define uiProperty(type, name) type * name;
#define uiSynthesize(scene, type, name) name = (type *)scene->findByName(#name);
#define uiValuator(type,name) type name;
#define uiBind(object,valuator) object->setValuator(&valuator);

void uiInit();
void uiExit();
void uiCleanUpUserData(puObject* object, const bool recursive = false);
const puObject* uiFindByName(const puObject* object, const char* name);

char* uiGetGLVersion();
char* uiGetGLExtensions();
bool uiCheckGLExtension(const char * extensionName);

void * uiGetGLProcAddress(const char* name);

char *uiStrCat ( const char *s1, const char* s2);

char *uiMakeResourcePath(const char* filename);

// -------------------------------------------------------------------------------------------------
//  uiLog
// -------------------------------------------------------------------------------------------------
enum eLogColors
{
    Success= 0,
    Debug,
    Warning,
    Error,
    Fatal,
    PlainText,
    Renderer
};

class uiLog
{
private:
	const char* m_szFilename;		//the log filename (set at initiation)
	bool m_bEnabled;		//is the log currently enabled?
	
	
	const char* getColor( eLogColors color ) const;
	
public:
	
	
	bool init( const char* szFilename );
	void write( eLogColors color, const char* szString, ... );
	
	inline void enable( void )
	{	m_bEnabled= true;	}
	
	inline void disable( void )
	{	m_bEnabled= false;	}
	
	inline bool isEnabled( void )
	{	return m_bEnabled;	}
	
	uiLog( void ) : m_bEnabled( false ), m_szFilename( 0 )
	{	}
	
	uiLog( char* szFilename ) : m_bEnabled( false ), m_szFilename( 0 )
	{	init( szFilename );	}
	
	~uiLog( void )
	{	}
};

extern uiLog Log;

// -------------------------------------------------------------------------------------------------
//  uiApplication
// -------------------------------------------------------------------------------------------------
class uiApplication
{
private:
	// Stop the compiler generating methods of copy the object
	uiApplication(uiApplication const& copy);            // Not Implemented
	uiApplication& operator=(uiApplication const& copy); // Not Implemented
	static uiApplication* instance;
	
	float x;
	float y;
	bool centered;
	float width;
	float height;
	bool fullscreen;
	const char* name;
	
	//multitexturing variables
	int	 numTextureUnits;
	bool canMultitexture;
	
	//compiled vertex array (CVA) flag
	bool canCVA;
	
	void loadConfig(const char* configFile);
	void loadDefaultConfig();
	void setupExtensions();
	
	static void _error_cb(enum ulSeverity severity, char* msg );
	
public:
	uiApplication(int argc, char *argv[]){assert(!uiApplication::instance); uiApplication::instance = this; init(argc, argv, 0);}
	uiApplication(int argc, char *argv[], const char* configFile){assert(!uiApplication::instance); uiApplication::instance = this; init(argc, argv, configFile);}
	virtual ~uiApplication();
	
	static uiApplication& getInstance()
	{
		assert(instance);
		return (*instance);
	}
	
	static uiApplication* getInstancePtr()
	{
		assert(instance);
		return instance;
	}
	
	virtual void init(int argc, char *argv[], const char* configFile = 0);
	virtual void loop();
	virtual void shutdown();
	
	virtual void display(void) = 0;
	virtual void idle(void) = 0;
	virtual void reshape(int w, int h) = 0;
	virtual void keyUp(unsigned char key, int x, int y) = 0;
	virtual void keyDown(unsigned char key, int x, int y) = 0;
	virtual void specialUp(unsigned char key, int x, int y) = 0;
	virtual void specialDown(unsigned char key, int x, int y) = 0;
	virtual void motion(int newx, int newy) = 0; 
	virtual void passive(int newx, int newy) = 0;
	virtual void mouse(int button, int state, int mouseX, int mouseY) = 0;
};

// -------------------------------------------------------------------------------------------------
//  uiTextureList
// -------------------------------------------------------------------------------------------------
class uiTexture
{
	friend class uiTextureList;
	
public:
	uiTexture(const char* name, ssgTexture* texture, const float left=0.0f, const float bottom=0.0f, const float right=0.0f, const float top=0.0f);
	virtual ~uiTexture();
	
	GLuint getHandle() { return texture->getHandle(); }
	
	float right;
	float bottom;
	float left;
	float top;
	
private:    
	const char* name;
	
	ssgTexture* texture;
};

// -------------------------------------------------------------------------------------------------
//  uiTextureList
// -------------------------------------------------------------------------------------------------
class uiTextureList: private ulList
{
public:
	uiTextureList ( int init = 3 )
	: ulList ( init )
	{}
	void add(uiTexture* texture);
	uiTexture* findByName ( const char* fname ) ;
};

// -------------------------------------------------------------------------------------------------
//  uiImage
// -------------------------------------------------------------------------------------------------
class uiImage
{
public:
	uiImage();
	uiImage(const char *name, const float width = 0.0f, const float height = 0.0f);
	virtual ~uiImage();
	
	virtual void draw(const float dx, const float dy);
	
	bool hitTest(int b, int s, int xCoord, int yCoord);
	
protected:
	bool hidden;
	
	uiTexture* texture;
	
	float width;
	float height;
	
	virtual void update(){};
	
	inline int pow2roundup(int x)
	{
		if (x < 0)
			return 0;
		--x;
		x |= x >> 1;
		x |= x >> 2;
		x |= x >> 4;
		x |= x >> 8;
		x |= x >> 16;
		return x+1;
	}
	
private:
	char *name;
};

// -------------------------------------------------------------------------------------------------
//  uiTextureAtlas
// -------------------------------------------------------------------------------------------------
typedef std::map<std::string, uiTexture*> uiTextureMap;
class uiTextureAtlas
{
public:
	uiTextureAtlas(const char* filename);
	virtual ~uiTextureAtlas();
	
	bool hasTexture(const char* name);
	uiTexture* getTexture(const char* name);
	
private:
	uiTextureMap texturesMap;
	
	ssgTexture* atlasTexture;
};

// -------------------------------------------------------------------------------------------------
//  uiTextureManager
// -------------------------------------------------------------------------------------------------
typedef std::map<std::string, uiTextureAtlas*> uiTextureAtlasMap;
class uiTextureManager
{
private:
	// Private Constructor
	//uiTextureManager();
	// Stop the compiler generating methods of copy the object
	uiTextureManager(uiTextureManager const& copy);            // Not Implemented
	uiTextureManager& operator=(uiTextureManager const& copy); // Not Implemented
	
	void init();
	
	uiTextureAtlasMap atlases;
	uiTextureMap textures;
	
	static uiTextureManager* instance;
	
public:
	uiTextureManager() { assert(!instance); instance = this; init();}
	~uiTextureManager();
	
	static uiTextureManager& getInstance()
	{
		assert(instance);
		// The only instance
		// Guaranteed to be lazy initialized
		// Guaranteed that it will be destroyed correctly
		//static uiTextureManager instance;
		//            if(!instance)
		//               instance = new uiTextureManager();
		
		//return instance;
		return (*instance);
	}
	
	static uiTextureManager* getInstancePtr()
	{
		assert(instance);
		return instance;
	}
	
	uiTexture* getTexture(const char* name) const;
	bool addTexture(const char* name, uiTexture* texture);
};

// -------------------------------------------------------------------------------------------------
//  uiData
// -------------------------------------------------------------------------------------------------
class uiView;
class uiData
{
public:
	uiData(const char* name, uiView* view, void * userData = 0);
	virtual ~uiData();
	
	char* name;
	uiView* view;
	void * userData;
};

// -------------------------------------------------------------------------------------------------
//  uiGroup
// -------------------------------------------------------------------------------------------------
class uiGroup : public puGroup
{
public:
	uiGroup(int x, int y) : puGroup(x, y) {}
};

// -------------------------------------------------------------------------------------------------
//  uiViewDelegate
// -------------------------------------------------------------------------------------------------
class uiViewDelegate
{
public:
	virtual void onCallback(const uiView* view, const char* name, puObject* object) = 0;
	virtual void onActiveCallback(const uiView* view, const char* name, puObject* object) = 0;
	virtual void onDownCallback(const uiView* view, const char* name, puObject* object) = 0;
};

// -------------------------------------------------------------------------------------------------
//  uiView
// -------------------------------------------------------------------------------------------------
class uiView : public puGroup
{
public:
	uiView();
	uiView(int x, int y, const float width, const float height);
	virtual ~uiView();
	
	void setDelegate(uiViewDelegate* delegate);
	
	virtual void display(void);
	virtual void resize(int w, int h);
	virtual void keyUp(unsigned char key, int x, int y);
	virtual void keyDown(unsigned char key, int x, int y);
	virtual void specialUp(int key, int x, int y);
	virtual void specialDown(int key, int x, int y);
	virtual void motion(int newx, int newy);
	virtual void passive(int newx, int newy);
	virtual void mouse(int button, int state, int mouseX, int mouseY);
	
	const puObject* findByName(const char* name) const;
	
	// Static XML parsing, factory pattern
	static uiView* createView(const char* filename);
	static uiView* createView(tinyxml2::XMLElement* element);
	static void parseElement(const tinyxml2::XMLElement* element, uiView* view, puObject* parent);
	static bool beginElement(const tinyxml2::XMLElement* element, uiView* view, puObject* parent, puObject** object);
	static void endElement(const tinyxml2::XMLElement* element, uiView* view, puObject* parent, puObject* object);
	static int dirnFromString(const char* dirn);
	static int styleFromString(const char* style);
	static int arrowTypeFromString(const char* arrowType);
	static void rgbaFromWebColor(sgVec3 color, const char* webColor);
	static int placeFromString(const char* place);
	static int sliderModeFromString(const char* mode);
	static int arrowsFromString(const char* arrows);
	
private:
	float width;
	float height;
	
	uiViewDelegate* delegate;
	
	// Callbacks
	static uiData* _get_data(puObject* object);
	static void _ui_cb ( puObject * );
	static void _ui_active_cb ( puObject * );
	static void _ui_down_cb ( puObject * );
};

// -------------------------------------------------------------------------------------------------
//  uiSceneDelegate
// -------------------------------------------------------------------------------------------------
class uiScene;
class uiSceneDelegate
{
public:
	virtual void onCallback(const uiScene* scene, const uiView* view, const char* name, puObject* object) = 0;
	virtual void onActiveCallback(const uiScene* scene, const uiView* view, const char* name, puObject* object) = 0;
	virtual void onDownCallback(const uiScene* scene, const uiView* view, const char* name, puObject* object) = 0;
};

// -------------------------------------------------------------------------------------------------
//  uiScene
// -------------------------------------------------------------------------------------------------
class uiScene : public uiViewDelegate
{
public:
	uiScene();
	uiScene(const char* filename);
	virtual ~uiScene();
	
	virtual void load(const char* filename);
	virtual void display(void);
	virtual void resize(int w, int h);
	virtual void keyUp(unsigned char key, int x, int y);
	virtual void keyDown(unsigned char key, int x, int y);
	virtual void specialUp(int key, int x, int y);
	virtual void specialDown(int key, int x, int y);
	virtual void motion(int newx, int newy); 
	virtual void passive(int newx, int newy);
	virtual void mouse(int button, int state, int mouseX, int mouseY);
	
	void setDelegate(uiSceneDelegate* delegate);
	
	const puObject* findByName(const char* name) const;
    
    ssgRoot* getRoot() const { return mRoot; }
    ssgContext* getContext() const { return mContext; }
	
	// uiViewDelegate
	void onCallback(const uiView* view, const char* name, puObject* object);
	void onActiveCallback(const uiView* view, const char* name, puObject* object);
	void onDownCallback(const uiView* view, const char* name, puObject* object);
	
protected:
	uiView *mView;
    ssgRoot *mRoot;
    ssgContext *mContext;
	
	uiSceneDelegate* delegate;
};

// -------------------------------------------------------------------------------------------------
//  uiButton
// -------------------------------------------------------------------------------------------------
class uiButton : public puButton
{
public:
	void draw ( int dx, int dy ) ;
    uiButton ( int minx, int miny, int maxx, int maxy);
	uiButton ( int minx, int miny, int maxx, int maxy, const char *nameNormal, const char *nameFocused, const char *nameClicked, const char *nameDisabled);
	
    void setNormalImage(const char* name, const float width=0, const float height=0);
	
private:
	uiImage* normalImage;
	uiImage* focusedImage;
	uiImage* clickedImage;
	uiImage* disabledImage;
};

// -------------------------------------------------------------------------------------------------
//  uiArrowButton
// -------------------------------------------------------------------------------------------------
class uiArrowButton : public puArrowButton
{
public:
    uiArrowButton ( int minx, int miny, int maxx, int maxy, int ptype) : puArrowButton ( minx, miny, maxx, maxy, ptype ) {}
};

// -------------------------------------------------------------------------------------------------
//  uiOneShot
// -------------------------------------------------------------------------------------------------
class uiOneShot : public puOneShot
{
public:
    uiOneShot ( int minx, int miny, int maxx, int maxy) : puOneShot(minx, miny, maxx, maxy) {}
};

// -------------------------------------------------------------------------------------------------
//  uiSlider
// -------------------------------------------------------------------------------------------------
class uiSlider : public puSlider
{
public:
    uiSlider ( int minx, int miny, int sz, int vertical, int width) : puSlider(minx, miny, sz, vertical, width) {}
};

// -------------------------------------------------------------------------------------------------
//  uiBiSlider
// -------------------------------------------------------------------------------------------------
class uiBiSlider : public puaBiSlider
{
public:
    uiBiSlider ( int minx, int miny, int sz, int vertical, int width) : puaBiSlider(minx, miny, sz, vertical, width) {}
	uiBiSlider ( int minx, int miny, int sz, int vertical) : puaBiSlider(minx, miny, sz, vertical) {}
};

// -------------------------------------------------------------------------------------------------
//  uiTriSlider
// -------------------------------------------------------------------------------------------------
class uiTriSlider : public puaTriSlider
{
public:
    uiTriSlider ( int minx, int miny, int sz, int vertical, int width) : puaTriSlider(minx, miny, sz, vertical, width) {}
	uiTriSlider ( int minx, int miny, int sz, int vertical) : puaTriSlider(minx, miny, sz, vertical) {}
};

// -------------------------------------------------------------------------------------------------
//  uiDial
// -------------------------------------------------------------------------------------------------
class uiDial : public puDial
{
public:
    uiDial ( int x, int y, int size) : puDial(x, y, size) {}
};

// -------------------------------------------------------------------------------------------------
//  uiFrame
// -------------------------------------------------------------------------------------------------
class uiFrame : public puFrame
{
public:
    uiFrame ( int minx, int miny, int maxx, int maxy) : puFrame(minx, miny, maxx, maxy) {}
};

// -------------------------------------------------------------------------------------------------
//  uiText
// -------------------------------------------------------------------------------------------------
class uiText : public puText
{
public:
    uiText ( int x, int y) : puText(x, y) {}
};

// -------------------------------------------------------------------------------------------------
//  uiInput
// -------------------------------------------------------------------------------------------------
class uiInput : public puInput
{
public:
    uiInput ( int minx, int miny, int maxx, int maxy) : puInput( minx, miny, maxx, maxy) {}
};

// -------------------------------------------------------------------------------------------------
//  uiLargeInput
// -------------------------------------------------------------------------------------------------
class uiLargeInput : public puaLargeInput
{
public:
    uiLargeInput ( int x, int y, int width, int height, int arrows, int slidersWidth, bool wrapText) : puaLargeInput( x, y, width, height, arrows, slidersWidth, wrapText) {}
};

// -------------------------------------------------------------------------------------------------
//  uiPopup
// -------------------------------------------------------------------------------------------------
class uiPopup : public puPopup
{
public:
    uiPopup ( int x, int y) : puPopup(x, y) {}
};

// -------------------------------------------------------------------------------------------------
//  uiDialogBox
// -------------------------------------------------------------------------------------------------
class uiDialogBox : public puDialogBox
{
public:
    uiDialogBox ( int x, int y) : puDialogBox(x, y) {}
};

// -------------------------------------------------------------------------------------------------
//  uiPopupMenu
// -------------------------------------------------------------------------------------------------
class uiPopupMenu : public puPopupMenu
{
public:
    uiPopupMenu ( int x, int y) : puPopupMenu(x, y) {}
};

// -------------------------------------------------------------------------------------------------
//  uiSubmenuItem
// -------------------------------------------------------------------------------------------------
class uiSubmenuItem
{
public:
	uiSubmenuItem(const char* name, const char* legend); 
	~uiSubmenuItem();
	
	char* getName() const { return name;}
	char* getLegend() const { return legend;}
	
private:
	char* name;
	char* legend;
};

// -------------------------------------------------------------------------------------------------
//  uiSubmenu
// -------------------------------------------------------------------------------------------------
class uiSubmenu
{
public:
	uiSubmenu(const char* str); 
	~uiSubmenu();
	void addItem(const char* name, const char* legend);
	unsigned int getNumItems() const;
	uiSubmenuItem* getItem(unsigned int i) const;
	const char* getName() {return name;}
	
private:
	const char* name;
	ulList* items;
};

// -------------------------------------------------------------------------------------------------
//  uiMenuBar
// -------------------------------------------------------------------------------------------------
class uiMenuBar : public puMenuBar
{
public:
	uiMenuBar(int h);
	~uiMenuBar();
	
	// Call it on close, since we need user data to be set in the menu bar
	void load(const tinyxml2::XMLElement* element);
	
	puObject* findByName(const char* name);
	void cleanUpUserData();
	
private:
	uiSubmenu* submenu; // current one
	ulList* submenus;
	
	void addSubmenu(const char* str);
	void addSubmenuItem(const char* name, const char* legend);
	void closeSubmenu();
	void addSeparator();
	
	void parseElement(const tinyxml2::XMLElement* element);
	void beginElement(const tinyxml2::XMLElement* element);
	void endElement(const tinyxml2::XMLElement* element);
};

// -------------------------------------------------------------------------------------------------
//  uiVerticalMenu
// -------------------------------------------------------------------------------------------------
class uiVerticalMenu : public puaVerticalMenu
{
public:
	uiVerticalMenu(int x, int y);
	~uiVerticalMenu();
	
	// Call it on close, since we need user data to be set in the vertical menu
	void load(const tinyxml2::XMLElement* element);
	
	puObject* findByName(const char* name);
	void cleanUpUserData();
	
private:
	uiSubmenu* submenu; // current one
	ulList* submenus;
	
	void addSubmenu(const char* str);
	void addSubmenuItem(const char* name, const char* legend);
	void closeSubmenu();
	void addSeparator();
	
	void parseElement(const tinyxml2::XMLElement* element);
	void beginElement(const tinyxml2::XMLElement* element);
	void endElement(const tinyxml2::XMLElement* element);
};

// -------------------------------------------------------------------------------------------------
//  uiListBox
// -------------------------------------------------------------------------------------------------
class uiListBox : public puListBox
{
public:
	uiListBox(int minx, int miny, int maxx, int maxy, char** list = NULL);
	~uiListBox();
	
	// Call it on close, since we need user data to be set in the list box
	void load(const tinyxml2::XMLElement* element);
	
	puObject* findByName(const char* name);
	void cleanUpUserData();
	
private:
	ulList* items;
    char** list;
	
	void close();
    void clear();
	
	void parseElement(const tinyxml2::XMLElement* element);
	void beginElement(const tinyxml2::XMLElement* element);
	void endElement(const tinyxml2::XMLElement* element);
};

// -------------------------------------------------------------------------------------------------
//  uiButtonBox
// -------------------------------------------------------------------------------------------------
class uiButtonBox : public puButtonBox
{
public:
	uiButtonBox(int minx, int miny, int maxx, int maxy, char** labels, int one_button);
	~uiButtonBox();
	
	// Call it on close, since we need user data to be set in the button box
	void load(const tinyxml2::XMLElement* element);
	
private:
	ulList* items;
    char** list;
	
	void close();
    void clear();
	
	void parseElement(const tinyxml2::XMLElement* element);
	void beginElement(const tinyxml2::XMLElement* element);
	void endElement(const tinyxml2::XMLElement* element);
};

// -------------------------------------------------------------------------------------------------
//  uiComboBox
// -------------------------------------------------------------------------------------------------
class uiComboBox : public puaComboBox
{
public:
	uiComboBox(int minx, int miny, int maxx, int maxy, char** labels, int editable=TRUE);
	~uiComboBox();
	
	// Call it on close, since we need user data to be set in the button box
	void load(const tinyxml2::XMLElement* element);
	
private:
	ulList* items;
    char** list;
	
	void close();
    void clear();
	
	void parseElement(const tinyxml2::XMLElement* element);
	void beginElement(const tinyxml2::XMLElement* element);
	void endElement(const tinyxml2::XMLElement* element);
};

// -------------------------------------------------------------------------------------------------
//  uiSelectBox
// -------------------------------------------------------------------------------------------------
class uiSelectBox : public puaSelectBox
{
public:
	uiSelectBox(int minx, int miny, int maxx, int maxy, char** labels);
	~uiSelectBox();
	
	// Call it on close, since we need user data to be set in the button box
	void load(const tinyxml2::XMLElement* element);
	void updateList(ulList* list, bool duplicate=FALSE);
	
private:
	ulList* items;
    char** list;
	
	void close();
    void clear();
	
	void parseElement(const tinyxml2::XMLElement* element);
	void beginElement(const tinyxml2::XMLElement* element);
	void endElement(const tinyxml2::XMLElement* element);
};

// -------------------------------------------------------------------------------------------------
//  uiFileSelectior
// -------------------------------------------------------------------------------------------------
class uiFileSelector : public puaFileSelector
{
public:
    uiFileSelector(int x, int y, int w, int h, int arrows, const char *dir, const char *title = "Pick a file");
	~uiFileSelector();
};


#endif /* defined(__trail_map_editor__ui__) */
